#include "StdAfx.h"
#include "ChunkBoundary.h"
#include "ChunkSpace.h"
#include "SpecifiedChunk.h"
#include "ChunkFileSerializer.h"
#include "ChunkManager.h"

#include "3rd_part\xml\TiXmlUtility.h"

using namespace ChunkSystem;
using namespace Common;

ChunkBoundary::ChunkBoundary(Chunk* pOwnerChunk)
	:m_pOwnerChunk(pOwnerChunk)
{
}


ChunkBoundary::~ChunkBoundary(void)
{
}

bool ChunkBoundary::Load( TiXmlNode* pChunkBoundaryData  )
{
	if (!pChunkBoundaryData)
	{
		m_BoundaryPlane = Plane( Vector3(0,0,0), 0 );
		return false;
	}

	m_BoundaryPlane = TiXmlUtility::ReadPlane( pChunkBoundaryData );
	m_BoundaryPlane.normalise();

	// prepare for some error checking
	bool someInternal = false;
	bool someExternal = false;

	// read any portals
	TiXmlHandle ptrChunkBoundary ( pChunkBoundaryData );

	for ( size_t iChunkBoundary = 0 ; ; ++iChunkBoundary )
	{
		TiXmlElement* pPortalData = ptrChunkBoundary.Child( "portal" , iChunkBoundary ).ToElement();
		if ( pPortalData == NULL )
			break;

		std::auto_ptr<Portal> ptrNewPortal ( new Portal( pPortalData, this ) );
		if ( ptrNewPortal->GetPortalType() == Portal::eInvasive )
			someInternal = true;
		else
			someExternal = true;

		Chunk* pBindChunk = ptrNewPortal->GetBindChunk();
		if( pBindChunk != NULL )
			m_boundPortals.push_back( ptrNewPortal.release() );
		else
			m_unboundPortals.push_back( ptrNewPortal.release() );
	}

	return !( someInternal && someExternal );
}

Chunk* ChunkBoundary::GetOwnerChunk(void)
{
	return m_pOwnerChunk;
}

void ChunkBoundary::BindPortal( size_t iUnbindPortalIdx )
{
	assert( m_unboundPortals.size() > iUnbindPortalIdx );

	Portal* pUnbindPortal = m_unboundPortals[iUnbindPortalIdx];
	m_boundPortals.push_back(pUnbindPortal);
	m_unboundPortals.erase( m_unboundPortals.begin() + iUnbindPortalIdx );
}

void ChunkBoundary::UnbindPortal( size_t iBindPortalIdx )
{
	assert( m_boundPortals.size() > iBindPortalIdx );

	Portal* pbindPortal = m_boundPortals[iBindPortalIdx];
	m_boundPortals.push_back(pbindPortal);
	m_unboundPortals.erase( m_boundPortals.begin() + iBindPortalIdx );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
Portal::Portal(TiXmlElement* pPortalData, ChunkBoundary* pOwnerBoundary)
	:m_pBindChunk(NULL),m_uAxis(Vector3::ZERO),m_vAxis(Vector3::ZERO),m_origin(Vector3::ZERO),
	m_localCentre(Vector3::ZERO),m_centre(Vector3::ZERO),m_pOwnerBoundary(pOwnerBoundary),m_ePortalType(eInvasive)
{
	assert( pPortalData != NULL );
	TiXmlHandle ptrPortalData( pPortalData );

	if( ptrPortalData.FirstChild("label").ToNode() != NULL )
		m_label = ptrPortalData.FirstChild("label").FirstChild().ToText()->Value();
	//String strInternal = ptrPortalData.FirstChild("internal").FirstChild().ToText()->Value();

	Chunk* pOwnerChunk = pOwnerBoundary->GetOwnerChunk();
	ChunkSpace* pOwnerChunkSpace = NULL;
	if( pOwnerChunk != NULL )
		pOwnerChunkSpace = pOwnerChunk->GetOwnerSpace();

	// read in the axes
	String strUAxis = ptrPortalData.FirstChild("uAxis").FirstChild().ToText()->Value();
	m_uAxis = TiXmlUtility::StringToVector3( strUAxis );

	Plane rPlane = m_pOwnerBoundary->GetPlane();
	m_vAxis = rPlane.normal.crossProduct( m_uAxis );
	m_origin = rPlane.normal * rPlane.d / rPlane.normal.squaredLength();

	Vector2 center;
	//read points 
	for( int i = 0; ; i++ )
	{
		if( ptrPortalData.Child("point", i).FirstChild().ToNode() == NULL )
			break;
		Vector3 vPoints = TiXmlUtility::StringToVector3( ptrPortalData.FirstChild("uAxis").FirstChild().ToText()->Value() );
		Vector2 vPtsOnBoudary = Vector2(vPoints.x,vPoints.z);
		m_conPortalPoints.push_back(vPtsOnBoudary);

		center += vPtsOnBoudary;
	}
	center /=  m_conPortalPoints.size();
	m_localCentre = center.x * m_uAxis + center.y * m_vAxis + m_origin;
	m_centre = m_localCentre;

	assert( pOwnerChunkSpace != NULL );

	// find out what to set pChunk to and new one
	String strConnectChunk;
	if( ptrPortalData.FirstChild("chunk").ToElement() != NULL )
	{
		strConnectChunk =  ptrPortalData.FirstChild("chunk").FirstChild().ToText()->Value();
		if( strConnectChunk == "heaven" )
			m_pBindChunk = pOwnerChunkSpace->GetHeaven();
		else if( strConnectChunk == "earth" )
			m_pBindChunk = pOwnerChunkSpace->GetEarth();
		else if( *strConnectChunk.rbegin() == 'o' )
		{
			m_pBindChunk = new ExternalChunk(strConnectChunk ,pOwnerChunkSpace);
		}
		else if( *strConnectChunk.rbegin() == 'i' )
		{
			//the owner chunk now maybe the outside chunk wrapper it.
			Chunk* pBoundaryOwnerChunk = pOwnerBoundary->GetOwnerChunk();
			InternalChunk* pInternalChunk = static_cast<InternalChunk*>( pBoundaryOwnerChunk );
			assert( pInternalChunk != NULL );
			ExternalChunk* pOwnerChunk = static_cast<ExternalChunk*>( pInternalChunk->GetOwner() );
			assert( pOwnerChunk != NULL );

			m_pBindChunk = new InternalChunk( strConnectChunk, pOwnerChunk );
		}
		else
			assert(!"unsupported.");
	}
	else
		m_pBindChunk = NULL;
}

Portal::~Portal()
{
}

Portal::PortalOutline& Portal::GetOutLine(void)
{
	return m_conPortalPoints;
}

Chunk* Portal::GetBindChunk(void) const
{
	return m_pBindChunk;
}

String Portal::GetLabel(void) const
{
	return m_label;
}

Portal::PortalType Portal::GetPortalType( void ) const
{
	return m_ePortalType;
}

void Portal::SetPortalType( PortalType eType )
{
	m_ePortalType = eType;
}

void Portal::BindChunk(Chunk* pTargetChunk)
{
	m_pBindChunk = pTargetChunk;
}

ChunkBoundary* Portal::GetOwnerBoundary( void ) const
{
	return m_pOwnerBoundary;
}

Vector3& Portal::GetLocalCenter(void)
{
	return m_localCentre;
}

Common::Vector3 Portal::GetAxisU( void ) const
{
	return m_uAxis;
}
Common::Vector3 Portal::GetAxisV( void ) const
{
	return m_vAxis;
}